`timescale 1ns/1ps

module test;

    // 参数定义
    localparam CLK_PERIOD = 10;  // 100MHz时钟
    localparam ADDR_WIDTH = 4;
    localparam DATA_WIDTH  = 32;
    localparam RAM_DEPTH_WORD = 16;
    // 定义从文件中读取矩阵数据进来的相关变量
    localparam BASE_ADDR_MATRIX_A = 4'h0;
    localparam BASE_ADDR_MATRIX_B = 4'h4;
    localparam BASE_ADDR_MATRIX_C = 4'h8;
    localparam BASE_ADDR_MATRIX_P = 4'hc;
    localparam MATRIX_SIZE        = 16;

    reg                      clk;
    reg                      rst_n;
    reg                      kick_start;
    wire                     ready;
    reg  [ADDR_WIDTH-1:0]    addr;
    reg  [DATA_WIDTH-1:0]    data_in;
    wire [DATA_WIDTH-1:0]    data_out;
    reg                      wr_en;
    reg                      rd_en;

    reg  [7:0]               orig_matrix_data_array [0:MATRIX_SIZE-1];
    reg  [DATA_WIDTH-1:0] 	 buf_size_4B;
    reg  [DATA_WIDTH-1:0]    read_data;
    // 定义matrix_a,matrix_b,matrix_c信号线，用于显示波形
    wire [DATA_WIDTH-1:0]    matrix_a_waveform[0:3];
    wire [DATA_WIDTH-1:0]    matrix_b_waveform[0:3];
    wire [DATA_WIDTH-1:0]    matrix_c_waveform[0:3];

    always #(CLK_PERIOD/2) clk = ~clk;

    mat_mul_oper #(
        .ADDR_WIDTH(ADDR_WIDTH),
        .DATA_WIDTH(DATA_WIDTH),
        .DEPTH(RAM_DEPTH_WORD)) mat_mul_inst (
        .clk(clk),
        .rst_n(rst_n),
        .kick_start(kick_start),
        .ready(ready),
        // 用户接口 - 简化操作
        .user_addr(addr),
        .user_data_in(data_in),
        .user_data_out(data_out),
        .kick_write(wr_en),
        .kick_read(rd_en),
        .matrix_a_output(matrix_a_waveform),
        .matrix_b_output(matrix_b_waveform),
        .matrix_c_output(matrix_c_waveform)
    );

    // 写操作任务
    task write_operation;
        input [ADDR_WIDTH-1:0] write_addr;
        input [DATA_WIDTH-1:0] write_data;
        begin
            // 等待mat_mul就绪
            wait(ready);
            repeat(1) @(posedge clk);
            
            // 设置写操作相关信号
            addr    = write_addr;
            data_in = write_data;
            wr_en   = 1;
            rd_en   = 0;
            
            //$display("Write start: addr=0x%h, data=0x%h mat_mul_ready %d ", write_addr, write_data, ready);
            
            // 等待操作完成
            repeat(1) @(posedge clk);
            wr_en = 0;
            wait(ready);
            //$display("write done:  addr=0x%h, data=0x%h mat_mul_ready %d ", write_addr, write_data, ready);
        end
    endtask

    // 读操作任务
    task read_operation;
        input [ADDR_WIDTH-1:0] read_addr;
        begin
            // 等待mat_mul就绪
            wait(ready);
            repeat(1) @(posedge clk);
            
            // 设置读操作相关信号
            addr = read_addr;
            wr_en = 0;
            rd_en = 1;
            
            //$display("Read start: addr=0x%h mat_mul_ready: %d ", read_addr, ready);
            
            // 等待操作完成和数据有效
            repeat(1) @(posedge clk);
            rd_en = 0;
            
            wait(ready);
            read_data = data_out;
            //$display("Read done:  read_data=0x%h data_out 0x%h ", read_data, data_out);
        end
    endtask

    task reset_orig_matrix_data_array;
        integer i;
        begin
            for (i = 0; i < MATRIX_SIZE; i = i + 1) begin
                orig_matrix_data_array[i] = 0;
            end
        end
    endtask

    task load_matrix_data_from_file;
        input string filename;
        input integer base_addr_in_ram;
        integer i;
        begin
            $readmemh(filename, orig_matrix_data_array);
            /* 从orig_matrix_data_array中每次取4字节，然后写入mem */
            buf_size_4B = 32'h0;
            for (i = 0; i < 4; i = i + 1) begin
                buf_size_4B[7 : 0] = orig_matrix_data_array[i * 4 + 3];
                buf_size_4B[15: 8] = orig_matrix_data_array[i * 4 + 2];
                buf_size_4B[23:16] = orig_matrix_data_array[i * 4 + 1];
                buf_size_4B[31:24] = orig_matrix_data_array[i * 4];
                /* 提取了4个字节的数据，并把他们组合到buf_size_4B，然后写入mem，从base_addr_in_ram开始写 */
                //$display("Fill matrix data to ram, addr:  0x%h matrix data: 0x%h", base_addr_in_ram + i, buf_size_4B);
                write_operation(base_addr_in_ram + i, buf_size_4B);
            end
        end
    endtask

    task print_ram;
        integer i;
        $display("\n=== Start Print RAM ===");
        for (i = 0; i < RAM_DEPTH_WORD; i = i + 1) begin
            read_operation(i);
            $display("addr:0x%h data: 0x%h", i, read_data);        
        end
        $display("\n=== End Print RAM ===");
    endtask

    task sync_to_rising_edge;
        input integer delay_clk_cycles;
        integer cycles;
        integer delay_ns;
        begin
            cycles = (delay_clk_cycles < 1) ? 1 : delay_clk_cycles;
            delay_ns = cycles * CLK_PERIOD;
            #delay_ns;
            //$display("kimi4: cycles:%d delay:%dns", cycles, delay_ns);
        end
    endtask

    initial begin
        //初始化
        clk = 1;
        rst_n = 1;
        addr = 0;
        data_in = 0;
        wr_en = 0;
        rd_en = 0;
        kick_start = 0;
        // 复位
        rst_n = 0;
        sync_to_rising_edge(100);
        rst_n = 1;
        $display("============================= MAC COMPUTING TEST START =============================");
        /*
        // 测试1: 写操作
        $display("\n[1] exec write operation ...");
        write_operation(4'h0, 32'hDEADBEEF);
        
        // 测试2: 读操作
        $display("\n[2] exec read operation ...");
        read_operation(4'h0);

        // 验证读取的数据
        if (read_data == 32'hDEADBEEF)
            $display("read data and verify successfully: 0x%h", read_data);
        else
            $display("read data failed: expectation=0xDEADBEEF, actuality=0x%h", read_data);
        
        // 测试3: 写操作
        $display("\n[3] exec write operation ...");
        write_operation(4'h4, 32'hDCCDBEEF);
        
        // 测试4: 读操作
        $display("\n[4] exec read operation ...");
        read_operation(4'h4);

        // 验证读取的数据
        if (read_data == 32'hDCCDBEEF)
            $display("read data and verify successfully: 0x%h", read_data);
        else
            $display("read data failed: expectation=0xDEADBEEF, actuality=0x%h", read_data);
        */
        reset_orig_matrix_data_array;
        //print_ram();
        
        $display("\nLoad matrix a to ram from file===");
        load_matrix_data_from_file("matrix_data_a.data", 4'h0);
        $display("\nLoad matrix b to ram from file===");
        load_matrix_data_from_file("matrix_data_b.data", 4'h4); 
        sync_to_rising_edge(1);
        //print_ram();
        for (integer i = 0; i < 1; i = i + 1) begin
            wait(ready);
            sync_to_rising_edge(2);
            $display("****************************************KICK TIME-%0d START****************************************", i+1);
            kick_start = 1;
            sync_to_rising_edge(1);
            kick_start = 0;
            sync_to_rising_edge(1);
            //$display("****************************************KICK TIME-%0d END ready-%0d ****************************************", i, ready);
        end
        #3000 
        $display("============================= MAC COMPUTING TEST END =============================");
        $finish;
    end

endmodule


